﻿using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Demo.SharePoint.Utilities;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using System.Runtime.InteropServices;

namespace Demo.SharePoint.Lists.OfficeLocations.OfficeLocationItemEventReceiver
{
    /// <summary>
    /// List Item Events
    /// </summary>
    [Guid("8fd19ccf-46a7-4cc7-8fd8-fd9f813d275a")]
    public class OfficeLocationItemEventReceiver : SPItemEventReceiver
    {
        /// <summary>
        /// Synchronous before event that occurs when a new item is added to its containing object.
        /// </summary>
        /// <param name="properties">
        /// A Microsoft.SharePoint.SPItemEventProperties object that represents properties of the event handler.
        /// </param>
        public override void ItemAdding(SPItemEventProperties properties)
        {
            if (ShouldGeoCode(properties))
                Geocode(properties);
        }

        /// <summary>
        /// Synchronous before event that occurs when an existing item is changed, for example, when the user changes data in one or more fields.
        /// </summary>
        /// <param name="properties">
        /// A Microsoft.SharePoint.SPItemEventProperties object that represents properties of the event handler.
        /// </param>
        public override void ItemUpdating(SPItemEventProperties properties)
        {
            if (ShouldGeoCode(properties))
                Geocode(properties);
        }

        private static void Geocode(SPItemEventProperties properties)
        {
            double latitude;
            double longitude;

            // Get the Google Api Key
            string key = GoogleMaps.GetApiKey(properties.OpenWeb());
            GoogleGeocoder geocoder = new GoogleGeocoder(key);

            try
            {
                geocoder.Geocode(Convert.ToString(properties.AfterProperties["WorkAddress"]),
                    //Convert.ToString(properties.AfterProperties["OtherAddressStreet"]),
                                 Convert.ToString(properties.AfterProperties["WorkCity"]),
                                 Convert.ToString(properties.AfterProperties["WorkState"]),
                                 Convert.ToString(properties.AfterProperties["WorkZip"]),
                                 out latitude,
                                 out longitude);
            }
            catch (Exception e)
            {
                string errMsg = "Error calculating latitude and longitude.  Manually enter the latitude and longitude.";
#if DEBUG
                errMsg += "\r\n\r\nException:\r\n" + e;
#endif
                properties.ErrorMessage = errMsg;
                properties.Cancel = true;
                return;
            }

            properties.AfterProperties["Latitude"] = latitude;
            properties.AfterProperties["Longitude"] = longitude;
        }

        private static bool ShouldGeoCode(SPItemEventProperties properties)
        {
            double beforeLatitude;
            double beforeLongitude;

            if (properties.EventType == SPEventReceiverType.ItemAdding)
            {
                if (double.TryParse(Convert.ToString(properties.AfterProperties["Latitude"]), out beforeLatitude) &&
                    double.TryParse(Convert.ToString(properties.AfterProperties["Longitude"]), out beforeLongitude))
                {
                    // User has manually entered values so no need to geocode
                    return false;
                }

                return true;
            }

            // Item Editing
            double.TryParse(Convert.ToString(properties.BeforeProperties["Latitude"] ?? properties.ListItem["Latitude"]), out beforeLatitude);
            double.TryParse(Convert.ToString(properties.BeforeProperties["Longitude"] ?? properties.ListItem["Longitude"]), out beforeLongitude);

            double afterLatitude;
            double afterLongitude = 0;

            // If values have been entered for latitude or longitude and they don't match the previous values
            // We can assume the user has entered values for latitude and longitude so we don't need to GeoCode
            // the address.
            if (double.TryParse(Convert.ToString(properties.AfterProperties["Latitude"]), out afterLatitude) &&
                double.TryParse(Convert.ToString(properties.AfterProperties["Longitude"]), out afterLongitude))
            {
                if (beforeLongitude != afterLongitude || beforeLatitude != afterLatitude)
                    return false;
            }

            // If user removes a latitude or longitude value, we need to GeoCode again.
            if (afterLongitude == 0 || afterLatitude == 0)
                return true;

            string streetAddress1 = Convert.ToString(properties.BeforeProperties["WorkAddress"] ?? properties.ListItem["WorkAddress"]);
            //string streetAddress2 = Convert.ToString(properties.BeforeProperties["OtherAddressStreet"] ?? properties.ListItem["OtherAddressStreet"]);
            string city = Convert.ToString(properties.BeforeProperties["WorkCity"] ?? properties.ListItem["WorkCity"]);
            string state = Convert.ToString(properties.BeforeProperties["WorkState"] ?? properties.ListItem["WorkState"]);
            string zip = Convert.ToString(properties.BeforeProperties["WorkZip"] ?? properties.ListItem["WorkZip"]);
            return streetAddress1 != Convert.ToString(properties.AfterProperties["WorkAddress"]) ||
                //streetAddress2 != Convert.ToString(properties.AfterProperties["OtherAddressStreet"]) ||
                   city != Convert.ToString(properties.AfterProperties["WorkCity"]) ||
                   state != Convert.ToString(properties.AfterProperties["WorkState"]) ||
                   zip != Convert.ToString(properties.AfterProperties["WorkZip"]);
        }
    }
}
